#pragma once
namespace graph_virtual
{
namespace astar_search
{
//you need this data structures to get data from astar algorithm
//vertex_data_container_t - data for vertices, which visited by astar algorithm, indexed by vert id
//vertex_debug_t - data to draw visited vertex and visuaize it's properties
template<typename value_type>
struct vertex_data_container_t
{
    typedef std::map< graph_basic::graph_id_t, std::map<graph_basic::vertex_in_graph_id_t, value_type> > data_t;
    data_t data_;
    bool has_key(graph_basic::vertex_id_t v_id);
    value_type get_value(graph_basic::vertex_id_t v_id);
    void set_value(graph_basic::vertex_id_t v_id, value_type val);
    value_type& operator[](graph_basic::vertex_id_t v_id);
    typename data_t::iterator begin();
    typename data_t::iterator end();
};

struct astar_vertex_data_t
{
    static const char NOT_VISITED = 0;
    static const char CLOSED_LIST = 1;
    static const char OPENED_LIST = 2;
    char color_;
    graph_basic::lat_lon_t distance_;
    graph_basic::vertex_id_t predecessor_;
    astar_vertex_data_t()
      :  distance_(graph_basic::MAX_FLOAT)
      ,  color_(astar_vertex_data_t::NOT_VISITED)
    {}
};

struct vertex_debug_t
{
    vertex_debug_t()
      :  distance_(0)
      ,  heuristic_(0)
      ,  lat_(0)
      ,  lon_(0)
      ,  id_(0, 0)
    {}
    graph_basic::lat_lon_t distance_;
    graph_basic::lat_lon_t heuristic_;
    graph_basic::lat_lon_t lat_;
    graph_basic::lat_lon_t lon_;
    graph_basic::vertex_id_t id_;
};


typedef std::pair<graph_basic::point_t, graph_basic::point_t> line_t; //line to draw in debugger
typedef std::vector<line_t> output_list_t; //collection of lines to draw in debugger
enum astar_mode_e
{
    debug,
    release
};
enum astar_direction_e
{
    undirectional,
    bidirectional
};

//the astar search algorithm
template<astar_mode_e mode, astar_direction_e direction>
struct astar_search_t
{
    astar_search_t(virtual_graph_view_ptr_t view, graph_basic::vertex_t& start_vertex, graph_basic::vertex_t& finish_vertex);
    void run(bool iterate, unsigned long long iteration_number);
    vertex_data_container_t<astar_vertex_data_t>& get_vertex_data();
    vertex_data_container_t<astar_vertex_data_t>& get_vertex_data2();
    output_list_t& get_output_list();
    output_list_t& get_output_list2(); 

    vertex_debug_t get_debug_vertex_by_id(graph_basic::vertex_id_t id, bool first_dim);
    graph_basic::vertex_t& get_vertex_by_id(graph_basic::vertex_id_t id);
    graph_basic::lat_lon_t get_potential(graph_basic::vertex_id_t id, bool for_forward_direct);
    graph_basic::vertex_id_t get_start_vertex_id() const;
    graph_basic::vertex_id_t get_finish_vertex_id() const;
    graph_basic::vertex_id_t get_fwd_search_cur_vertex_id() const;
    graph_basic::vertex_id_t get_bkwd_search_cur_vertex_id() const;
    
    bool is_all_ended();
    
    //statistics
    graph_basic::graph_int_t num_iterates_;
    graph_basic::graph_int_t num_iterates2_;

    graph_basic::graph_int_t visited_vertices_; // i am lazy to draft this strange mistake
    graph_basic::graph_int_t visited_vertices2_;

    graph_basic::graph_int_t visited_edges_;
    graph_basic::graph_int_t visited_edges2_;

    vertex_debug_t deb_vertex_current1_;
    vertex_debug_t deb_vertex_current2_;
    vertex_debug_t deb_vertex_prev1_;
    vertex_debug_t deb_vertex_prev2_;

    std::vector<vertex_debug_t> deb_vertex_prev1_adj;
    std::vector<vertex_debug_t> deb_vertex_prev2_adj;

    graph_basic::vertex_id_t prev_vertex1_id_;
    graph_basic::vertex_id_t prev_vertex2_id_;

        //Data continers statistics
    graph_basic::graph_int_t opened_list1_put_calls_;
    graph_basic::graph_int_t opened_list1_isin_calls_;
    graph_basic::graph_int_t opened_list1_extract_min_calls_;
    graph_basic::graph_int_t opened_list1_chpriority_calls_;
    graph_basic::graph_int_t opened_list1_max_elems_;
    graph_basic::graph_int_t closed_list1_put_calls_;
    graph_basic::graph_int_t closed_list1_isin_calls_;
    graph_basic::graph_int_t put_predecessor_calls_;
    graph_basic::graph_int_t get_distance_calls_;
    
private:
    //const for 2dim astar
    graph_basic::lat_lon_t beta_;
    graph_basic::lat_lon_t sf_dist_;
    //Algorithm state
    graph_basic::vertex_id_t current_v_id;
    graph_basic::vertex_id_t current_v2_id;
    bool is_all1_ended;
    bool is_all2_ended;
    //list of vertexpairs (edges) to draw them
    output_list_t output_list_;
    output_list_t output_list2_;
    //end alg state
    vertex_data_container_t<astar_vertex_data_t> vertex_data1_;
    vertex_data_container_t<astar_vertex_data_t> vertex_data2_;
    typedef std::map<graph_basic::vertex_id_t, graph_basic::lat_lon_t> distance_map_t;
    //typedef BinaryHeap distance_map_t;
    distance_map_t opened_list1_; 
    distance_map_t opened_list2_;

    virtual_graph_view_ptr_t view_;

    void init_state();
    graph_basic::lat_lon_t potential_fwd(graph_basic::vertex_t& v);
    graph_basic::lat_lon_t potential_revs(graph_basic::vertex_t& v);
    graph_basic::vertex_id_t extract_min();
    graph_basic::vertex_id_t extract_min2();
    void put_in_opened_list(graph_basic::vertex_id_t id);
    void put_in_opened_list2(graph_basic::vertex_id_t id);
    void put_in_closed_list(graph_basic::vertex_id_t id);
    void put_in_closed_list2(graph_basic::vertex_id_t id);
    bool is_in_opened_list(graph_basic::vertex_id_t id);
    bool is_in_opened_list2(graph_basic::vertex_id_t id);
    bool is_in_closed_list(graph_basic::vertex_id_t id);
    bool is_in_closed_list2(graph_basic::vertex_id_t id);
    bool is_opened_list_empty();
    bool is_opened_list2_empty();
    void notify_priority_changed(graph_basic::vertex_id_t id);
    void notify_priority_changed2(graph_basic::vertex_id_t id);
    graph_basic::lat_lon_t get_distance(graph_basic::vertex_id_t id);
    graph_basic::lat_lon_t get_distance2(graph_basic::vertex_id_t id);
    void put_distance(graph_basic::vertex_id_t id, graph_basic::lat_lon_t dist);
    void put_distance2(graph_basic::vertex_id_t id, graph_basic::lat_lon_t dist);
    void put_predecessor(graph_basic::vertex_id_t id_v, graph_basic::vertex_id_t id_pred);
    void put_predecessor2(graph_basic::vertex_id_t id_v, graph_basic::vertex_id_t id_pred);
    void collect_stats1();
    void process_current_adjacent1();
    void run1(bool iterate, unsigned long long iteration_number);
    void collect_stats2();
    void process_current_adjacent2();
    void run2(bool iterate, unsigned long long iteration_number);
    graph_basic::vertex_id_t start_vertex_id_;
    graph_basic::vertex_id_t finish_vertex_id_;
    graph_basic::lat_lon_t finish_lat_;
    graph_basic::lat_lon_t finish_lon_;
    graph_basic::lat_lon_t start_lat_;
    graph_basic::lat_lon_t start_lon_;

    graph_basic::lat_lon_t min_2direct_dist;
    graph_basic::vertex_id_t sh_path_vert_dim1;
    graph_basic::vertex_id_t sh_path_vert_dim2;
};

/*
params:
@list - std:::list<lat_lon_t>, contained result
*/
template<astar_mode_e mode, astar_direction_e direction>
inline void fill_shpath(astar_search_t<mode, direction>& as, std::list<graph_basic::lat_lon_t>& list);
}
}
